package com.atung.service.impl;

import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.spec.InvalidParameterSpecException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.shiro.codec.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atung.dao.AdmSchoolDAO;
import com.atung.dao.SchoolUserDAO;
import com.atung.model.AdmSchool;
import com.atung.model.BusSchoolUser;
import com.atung.model.WxLoginDTO;
import com.atung.request.RequestWxLogin;
import com.atung.request.RequestWxRegister;
import com.atung.response.ResponseChangeSchool;
import com.atung.response.ResponseGetAccessToken;
import com.atung.response.ResponseWxLogin;
import com.atung.service.UserLoginService;
import com.atung.tool.Dictionaries;
import com.atung.tool.wxUrl.WxUrlTool;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;

@Service
public class UserLoginServiceImpl implements UserLoginService {
    private Logger logger = LoggerFactory.getLogger(getClass());
	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	
    @Value("${wx.appid}")
    private String appid;
    @Value("${wx.secret}")
    private String secret;
    
	@Autowired
	private SchoolUserDAO schoolUserDAO;
	@Autowired
	private AdmSchoolDAO admSchoolDAO;
	//获取access_token
	private  String getAccessTokenURL="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
	
	
	@Override
	public ResponseWxLogin selectUserAndOpenId(WxLoginDTO  loginMessage,RequestWxLogin request)throws Exception {
		// TODO 自动生成的方法存根
		
		
		BusSchoolUser busSchoolUser=schoolUserDAO.selectUserAndOpenId(loginMessage.getOpenid());
		ResponseWxLogin date=new ResponseWxLogin();
		String userInfoString = getUserInfo(request.getEncryptedData(),loginMessage.getSession_key(),request.getIv());
		if(Objects.isNull(busSchoolUser)){
			String accessToken=stringRedisTemplate.opsForValue().get(Dictionaries.schoolAccessTokenRedisKey);
			if(!stringRedisTemplate.hasKey(Dictionaries.schoolAccessTokenRedisKey)){
				String accessTokenUrl=WxUrlTool.GetAccessToken.replace("APPID", appid).replace("SECRET", secret);
				String response=HttpUtil.get(accessTokenUrl);
				ResponseGetAccessToken  AccessToken=JSONUtil.toBean(response, ResponseGetAccessToken.class);
				accessToken=AccessToken.getAccess_token(); 
				stringRedisTemplate.opsForValue().set(Dictionaries.schoolAccessTokenRedisKey,AccessToken.getAccess_token(),AccessToken.getExpires_in(), TimeUnit.MILLISECONDS);
			}
			BusSchoolUser insertUser=new BusSchoolUser();

			if(Objects.nonNull(userInfoString)){
				JSONObject userInfo=JSON.parseObject(userInfoString);
				String nickName = userInfo.getString( "nickName" );
		        String avatarUrl = userInfo.getString( "avatarUrl" );
		        String gender  = userInfo.getString( "gender" );
		        nickName = Base64.encodeToString(nickName.getBytes());
				insertUser.setWxName(nickName);
				insertUser.setWxPhoto(avatarUrl);
				insertUser.setUserSex(Integer.parseInt(gender));
			}
			insertUser.setOpenId(loginMessage.getOpenid());
			insertUser.setCreationTime(LocalDateTime.now());
			Boolean insertResult=schoolUserDAO.insertUser(insertUser);
			if(!insertResult){
				//新增用户失败
				throw new Exception("ERR INSERT USERMESSAGE");
			}
		
			date.setOpenid(loginMessage.getOpenid());
			date.setIsRegister(true);
			return date;
		}
		date.setOpenid(busSchoolUser.getOpenId());
		date.setIsRegister(false);
		return date;
	}

	@Override
	public void updatetUserAndOpenId(RequestWxRegister request) {
		// TODO 自动生成的方法存根
		
		BusSchoolUser updateUser=new BusSchoolUser();
		BeanUtil.copyProperties(request, updateUser);
		updateUser.setModificationTime(LocalDateTime.now());
		schoolUserDAO.updatetUserAndOpenId(updateUser);
	}

	@Override
	public List<ResponseChangeSchool> selectSchoolBylonAndlat(String lon, String lat) {
		// TODO 自动生成的方法存根
		List<AdmSchool> admSchoolList=new ArrayList<>();
		for(int scope=1;scope<50;scope++ ){
			admSchoolList =admSchoolDAO.selectSchoolBylonAndlat(lon,lat,scope);
			if(admSchoolList.size()!=0){
				continue;
			}
		}
		List<ResponseChangeSchool>changeSchoolList=new ArrayList<>();
		for(AdmSchool admSchool: admSchoolList){
			ResponseChangeSchool changeSchool=new ResponseChangeSchool();
			BeanUtil.copyProperties(admSchool, changeSchool);
			changeSchool.setSchoolId(admSchool.getId());
			changeSchoolList.add(changeSchool);
		}
		return changeSchoolList;
	}
	
	/**
	 * h获取用户信息
	 * @param encryptedData
	 * @param sessionKey
	 * @param iv
	 * @return
	 */
	public String getUserInfo(String encryptedData, String sessionKey, String iv) {
	    String result = "";
	    // 被加密的数据
	    byte[] dataByte = Base64.decode(encryptedData);
	    // 加密秘钥
	    byte[] keyByte = Base64.decode(sessionKey);
	    // 偏移量
	    byte[] ivByte = Base64.decode(iv);
	    try {
	        // 如果密钥不足16位，那么就补足. 这个if 中的内容很重要
	        int base = 16;
	        if (keyByte.length % base != 0) {
	            int groups = keyByte.length / base
	                    + (keyByte.length % base != 0 ? 1 : 0);
	            byte[] temp = new byte[groups * base];
	            Arrays.fill(temp, (byte) 0);
	            System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
	            keyByte = temp;
	        }
	        // 初始化
	        Security.addProvider(new BouncyCastleProvider());
	        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
	        SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
	        AlgorithmParameters parameters = AlgorithmParameters
	                .getInstance("AES");
	        parameters.init(new IvParameterSpec(ivByte));
	        cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
	        byte[] resultByte = cipher.doFinal(dataByte);
	        if (null != resultByte && resultByte.length > 0) {
	            result = new String(resultByte, "UTF-8");
	        }
	    } catch (NoSuchAlgorithmException e) {
	        logger.error(e.getMessage(), e);
	    } catch (NoSuchPaddingException e) {
	        logger.error(e.getMessage(), e);
	    } catch (InvalidParameterSpecException e) {
	        logger.error(e.getMessage(), e);
	    } catch (IllegalBlockSizeException e) {
	        logger.error(e.getMessage(), e);
	    } catch (BadPaddingException e) {
	        logger.error(e.getMessage(), e);
	    } catch (UnsupportedEncodingException e) {
	        logger.error(e.getMessage(), e);
	    } catch (InvalidKeyException e) {
	        logger.error(e.getMessage(), e);
	    } catch (InvalidAlgorithmParameterException e) {
	        logger.error(e.getMessage(), e);
	    } catch (NoSuchProviderException e) {
	        logger.error(e.getMessage(), e);
	    }
	    return result;
	}
	/**
	 * 获取accessToken
	 * @return
	 * @throws Exception
	 */
	public String getAccessToken() {
		if(!stringRedisTemplate.hasKey(Dictionaries.schoolAccessTokenRedisKey)){
			return stringRedisTemplate.opsForValue().get(Dictionaries.schoolAccessTokenRedisKey);
		};
		String response=HttpUtil.get(getAccessTokenURL.replace("APPID", appid).replace("SECRET", secret));
		ResponseGetAccessToken  AccessToken=JSONUtil.toBean(response, ResponseGetAccessToken.class);

		stringRedisTemplate.opsForValue().set(Dictionaries.schoolAccessTokenRedisKey, AccessToken.getAccess_token(),AccessToken.getExpires_in(), TimeUnit.MILLISECONDS);
		return AccessToken.getAccess_token();
	}
}
